package core;

import constant.Constant;
import util.FileUtils;
import util.HttpUtils;
import util.LogUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.concurrent.*;

/**
 * 下载器类
 * Created by Lenovo
 * DATA:2023/10/19
 **/
public class Downloader {
    private ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(1);
    //线程池对象
    public ThreadPoolExecutor poolExecutor=new ThreadPoolExecutor(Constant.THREAD_NUM,Constant.THREAD_NUM,0,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5));
    public void download(String url) throws IOException {
        //获取文件的名字
        String httpFileName = HttpUtils.getHtpFileName(url);//从文件的下载路径中截取。
//        文件的下载路径
        httpFileName= Constant.PATH+httpFileName;//就是我设置的本地的下载文件夹的名字加上下载文件的名字。
        //获取本地文件的大小。
        long localFileLength= FileUtils.getFileContextLength(httpFileName);
//        获取链接对象
        HttpURLConnection httpURLConnection=null;
        DownloadInfoThread downloadInfoThread=null;
        try{
         httpURLConnection = HttpUtils.getHttpURLConnection(url);
         //获取下载文件的总大小
        int contextLength=httpURLConnection.getContentLength();
        //判断文件是是不是已经下载过了。
        if(localFileLength>=contextLength){
            LogUtils.info("{}文件已经下载过了",httpFileName);
            return;
        }
        //创建获取下载信息的任务对象。
        downloadInfoThread = new DownloadInfoThread(contextLength);
        //件任务交给线程执行，每隔一秒执行一次。
        scheduledExecutorService.scheduleAtFixedRate(downloadInfoThread,1,1, TimeUnit.SECONDS);
        //切分任务
        ArrayList<Future> futureArrayList = new ArrayList<>();
        split(url,futureArrayList);
        for (Future future : futureArrayList) {
            try {
                future.get();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        }
        //合并文件：
        if(merge(httpFileName)){
            clearTemp(httpFileName);
        }

        }finally {
            System.out.print("下载完成。");
            if(httpURLConnection!=null){
                httpURLConnection.disconnect();//关闭连接对象。
            }
            scheduledExecutorService.shutdownNow();//关闭
            poolExecutor.shutdown();//关闭线程池
        }
    }
    public void split(String url, ArrayList<Future> futureArrayList) throws IOException {
        //获取下载文件的大小。
        try{
            long httpFileContentLength = HttpUtils.getHttpFileContentLength(url);
            //计算切分后的文件大小。
            long size=httpFileContentLength/Constant.THREAD_NUM;
            //计算分块个数。
            for(int i=0;i<Constant.THREAD_NUM;i++){
                long startPos=i*size;//计算下载的起始位置
                long endPos;//计算结束位置
                if(i==Constant.THREAD_NUM-1){
                    //下载最后一块，下载剩余的部分。
                    endPos=0;
                }else{
                    endPos=startPos+size;
                }
                //如果不是第一块，起始位置要+1;
                if(startPos!=0){
                    startPos++;
                }
                //创建任务。
                DownloaderTask downloaderTask = new DownloaderTask(url, startPos, endPos, i);
//                将任务提交到线程池中
                Future<Boolean> submit = poolExecutor.submit(downloaderTask);
                futureArrayList.add(submit);
            }
        }catch (IOException e){
            e.printStackTrace();
        }

    }

    /**
     * 合并代码
     */
    public boolean merge(String fileName) {
        LogUtils.info("开始合并文件{}", fileName);
        byte[] bytes = new byte[Constant.BYTE_SIZE];
        int len = -1;
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(fileName, "rw")) {
            for (int i = 0; i < Constant.THREAD_NUM; i++) {
                try(BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(fileName+".temp"+i))){
                        while ((len = bufferedInputStream.read(bytes)) != -1) {
                            randomAccessFile.write(bytes, 0, len);
                        }
                   }
               }
            LogUtils.info("文件合成完毕{}"+fileName);
            }catch (Exception e){
                e.printStackTrace();
                return false;
            }
        return false;
        }
    /**
     * 清除文件。
     * @param fileName
     * @return
     */
    public boolean clearTemp(String fileName){
            for (int i = 0; i <Constant.THREAD_NUM ; i++) {
                new File(fileName+".temp"+i).delete();
            }
            return true;
        }
}
